package comp512.classes;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import comp512.gui.ClientGUI_Lobby;
import comp512.gui.ClientGUI_Session;

public class Player implements Runnable, Serializable {

	private static final long serialVersionUID = 2L;

	// Networking information
	private Socket socket;
	private String serverAddress;
	private Integer serverPort;

	// Player information
	private String id;
	private String sessionId;
	private List<String> teamMessages;
	private List<String> globalMessages;
	private String teamName;
	private Statistics statistics;
	private Boolean isAnswering;
	
	//Player instance
	private static Player playerInstance = null;
	
	//Game Information
	Game game;
	
	//GUI Manipulation
	private ClientGUI_Lobby lobbyInstance;
	private ClientGUI_Session sessionInstance;

	public Player(String serverAddress, int serverPort, String id)
			throws UnknownHostException, IOException {
		if(playerInstance == null)
			playerInstance = this;
		
		try {
			playerInstance.serverAddress = serverAddress;
			playerInstance.serverPort = serverPort;
			playerInstance.id = id;
			playerInstance.socket = new Socket(this.serverAddress, this.serverPort);
			playerInstance.sessionId = null;
			playerInstance.teamMessages = new ArrayList<>();
			playerInstance.globalMessages = new ArrayList<>();
			playerInstance.teamName = "";
			playerInstance.statistics = new Statistics();
			playerInstance.isAnswering = false;
			playerInstance.sendMessage(Flags.LOGIN_MESSAGE_FLAG, id);
			new Thread(playerInstance).start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Player(Player player) {
	}

	public void disconnect() {
		System.out.println("Shutting Down...");
		try {
			if (playerInstance.socket != null && !playerInstance.socket.isClosed()) {
				
				playerInstance.sendMessage(Flags.DISCONNECT_MESSAGE_FLAG, "Disconnecting");

				if (!playerInstance.socket.isClosed())
					playerInstance.socket.shutdownOutput();
				if (!playerInstance.socket.isClosed())
					playerInstance.socket.shutdownInput();
				if (!playerInstance.socket.isClosed())
					playerInstance.socket.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void sendMessage(String flag, String message) throws IOException {
		Message messageToSend = new Message(flag, playerInstance, message);
		try {
			if(playerInstance.socket.getOutputStream() == null && !playerInstance.socket.isConnected()){
				return;
			}
			if (playerInstance.socket.isConnected()){
				ObjectOutputStream out = new ObjectOutputStream(playerInstance.socket.getOutputStream());
				out.writeObject(messageToSend);			}
		} catch (SocketException se) {
			//TODO
			System.err.println(se.getMessage());
			se.printStackTrace();
		}
	}

	private void handleReceivedMessages(Message message) throws ClassNotFoundException, IOException {
		switch (message.getFlag()) {
			
			case Flags.RECEIVE_TEAM_MESSAGE_FLAG:
				playerInstance.teamMessages.add(message.getMessage());
				break;
	
			case Flags.RECEIVE_GLOBAL_MESSAGE_FLAG:
				playerInstance.globalMessages.add(message.getMessage());
				break;
	
			case Flags.RECEIVE_FRIEND_INVITE_FLAG:
				// TODO
				break;
	
			case Flags.RECEIVE_NEW_QUESTION_FLAG:
				// TODO
				break;
	
			case Flags.RECEIVE_NEW_SESSION_FLAG:
				//TODO
				break;
				
			case Flags.RECEIVE_LOGIN_SUCCESSFUL_FLAG:
				playerInstance.setStatistics(message.getPlayer().getStatistics());
				break;
				
			case Flags.RECEIVE_LIST_OF_SESSIONS_FLAG:
				//TODO
				break;
				
			default:
				// TODO
				break;
			}
	}

	//Object Overriders
	@Override
	public boolean equals(Object obj) {
		Player playerToCompare = (Player) obj;
		return playerInstance.getPlayerId().equals(playerToCompare.getPlayerId());
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return super.toString();
	}
	
	//Serializing Object
	@SuppressWarnings("unchecked")
	private void readObject(java.io.ObjectInputStream ois)
			throws ClassNotFoundException, IOException {
		id = (String) ois.readObject();
		sessionId = (String) ois.readObject();
		teamMessages = (List<String>) ois.readObject();
		globalMessages = (List<String>) ois.readObject();
		teamName = (String) ois.readObject();
		statistics = (Statistics) ois.readObject();
		isAnswering = (Boolean) ois.readObject();
	}

	private void writeObject(java.io.ObjectOutputStream oos) throws IOException {
		oos.writeObject(id);
		oos.writeObject(sessionId);
		oos.writeObject(teamMessages);
		oos.writeObject(globalMessages);
		oos.writeObject(teamName);
		oos.writeObject(statistics);
		oos.writeObject(isAnswering);
	}

	//Player Thread
	@Override
	public void run() {
		while (true) {
			try {
				if (playerInstance.socket.isConnected() && !playerInstance.socket.isClosed()) {
					ObjectInputStream in = new ObjectInputStream(playerInstance.socket.getInputStream());
					Message message = (Message) in.readObject();
					if(message != null){
						synchronized (playerInstance) {
							playerInstance.handleReceivedMessages(message);							
						}
					}
					in.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	
	// Server information getters
	public Socket getSocket() {
		return playerInstance.socket;
	}

	public String getServerAddress() {
		return playerInstance.serverAddress;
	}

	public int getServerPort() {
		return playerInstance.serverPort;
	}

	// Player information getters
	public String getPlayerId() {
		return playerInstance.id;
	}

	public String getSessionId() {
		return playerInstance.sessionId;
	}

	public List<String> getTeamMessages() {
		return playerInstance.teamMessages;
	}

	public List<String> getGlobalMessasages() {
		return playerInstance.globalMessages;
	}

	public String getTeamName() {
		return playerInstance.teamName;
	}

	public Statistics getStatistics() {
		return playerInstance.statistics;
	}

	public boolean getIsAnswering() {
		return playerInstance.isAnswering;
	}
	
	public static Player getInstance(){
		return playerInstance;
	}
	//Get game
	public Game getGame(){
		return playerInstance.game;
	}

	// Server information setters
	public void setSocket(Socket socket) {
		this.socket = socket;
	}

	public void setServerAddress(String serverAddress) {
		playerInstance.serverAddress = serverAddress;
	}

	public void setServerPort(int serverPort) {
		playerInstance.serverPort = serverPort;
	}

	// Player information setters
	public void setPlayerId(String playerId) {
		playerInstance.id = playerId;
	}

	public void setSessionId(String sessionId) {
		playerInstance.sessionId = sessionId;
	}

	public void setTeamMessages(List<String> teamMessages) {
		playerInstance.teamMessages = teamMessages;
	}

	public void setGlobalMessages(List<String> globalMessages) {
		playerInstance.globalMessages = globalMessages;
	}

	public void setTeamName(String teamName) {
		playerInstance.teamName = teamName;
	}

	public void setStatistics(Statistics statistics) {
		playerInstance.statistics = statistics;
	}

	public void setIsAnswering(boolean isAnswering) {
		playerInstance.isAnswering = isAnswering;
	}
	
	//Game setter
	public void setGame(Game game){
		playerInstance.game = game;
	}
	
	//Session GUI setter
	public void setSessionGUI(ClientGUI_Session session){
		playerInstance.sessionInstance = session;
	}
	
	public void setLobbyGUI(ClientGUI_Lobby lobby){
		playerInstance.lobbyInstance = lobby;
	}
	
	public static void setPlayerInstance(Player player){
		playerInstance = player;
	}

}
